/*
 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
 * Copyright (c) 1991-2000 University of Maryland at College Park
 * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
 * Copyright (c) 2013-2016 Carbonite, Inc.  All Rights Reserved.
 * All Rights Reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of U.M. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  U.M. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: James da Silva, Systems Design and Analysis Group
 *                         Computer Science Department
 *                         University of Maryland at College Park
 */
/*
 * $Id: conffile.h,v 1.72 2006/07/26 15:17:37 martinea Exp $
 *
 * interface for config file reading code
 */
#ifndef CONFFILE_H
#define CONFFILE_H

#include "amanda.h"
#include "amutil.h"

/* Getting Configuration Values
 * ============================
 *
 * Amanda configurations consist of a number of "global" parameters, as well as
 * named subsections of several types.  The global parameters are fetched with
 * the getconf_CONFTYPE functions, keyed by a confparam_t constant (with prefix
 * CNF_).  The subsection parameters are fetched with SUBSEC_get_PARAM()
 * macros, e.g., tapetype_get_blocksize(ttyp), where the argument comes from
 * lookup_SUBSEC(), in this case lookup_tapetype(name).
 *
 * Types
 * =====
 *
 * This module juggles two kinds of types: C types and conftypes.  Conftypes include
 * everything from integers through property lists, and are specific to the needs of
 * the configuration system.  Each conftype has a corresponding C type, which is of course
 * necessary to actually use the data.
 *
 * The val_t__CONFTYPE macros represent the canonical correspondance of conftypes to C
 * types, but in general the relationship is obvious: ints, strings, reals, and so forth
 * are represented directly.  Enumerated conftypes are represented by the corresponding
 * C enum type.  The 'rate' conftype is represented as a 2-element array of doubles, and
 * the 'intrange' conftype is represented as a 2-element array of ints.  exincludes are
 * a exinclude_t *, and a proplist is represented as a GHashTable *.
 *
 * Memory
 * ======
 * Note that, unless specified, all memory in this module is managed by the module
 * itself; return strings should not be freed by the caller.
 *
 * Error Handling
 * ==============
 * All errors and warnings generated by this module are available from get_config_errors().
 * It is up to the caller to route these messages to the user.  The function
 * config_print_errors() will print the errors to stderr, as a convenience.
 */

/*
 * Generic values
 *
 * This module uses a generic val_t type to hold values of various types -- it's basically
 * a union with type information and a 'seen' flag.  In a way, it's a very simple equivalent
 * to Glib's GValue.  It's worth considering rewriting this with GValue, but for the moment,
 * this works and it's here.
 */

typedef enum {
    RETENTION_NO           = 0,
    RETENTION_NO_REUSE     = (1<<0),
    RETENTION_TAPES        = (1<<1),
    RETENTION_DAYS         = (1<<2),
    RETENTION_RECOVER      = (1<<3),
    RETENTION_FULL         = (1<<4),
    RETENTION_CMD_COPY     = (1<<5),
    RETENTION_CMD_FLUSH    = (1<<6),
    RETENTION_CMD_RESTORE  = (1<<7),
    RETENTION_OTHER_CONFIG = (1<<8)
} RetentionType;
#define RETENTION_ALL (RETENTION_NO_REUSE|RETENTION_TAPES|RETENTION_DAYS|RETENTION_RECOVER|RETENTION_FULL|RETENTION_CMD_COPY|RETENTION_CMD_FLUSH|RETENTION_CMD_RESTORE|RETENTION_OTHER_CONFIG)

/* A "seen" struct.  Rather than allocate strings all over the place, this
 * string is in the "parsed_filenames" GSList and will be freed when that
 * GSList is freed.  This struct should be opaque to other modules. */
typedef struct seen_s {
    char *block;
    char *filename;
    int linenum;
} seen_t;

/* boolean and no_yes_all */
typedef enum {
    VALUE_NO,
    VALUE_YES,
    VALUE_ALL
} no_yes_all_t;

/* priority types */
typedef enum {
    PRIORITY_LOW,               /* Always direct to tape  */
    PRIORITY_MEDIUM,            /* If possible            */
    PRIORITY_HIGH               /* Always to holding disk */
} priority_t;

/* holdingdisk types */
typedef enum {
    HOLD_NEVER,                 /* Always direct to tape  */
    HOLD_AUTO,                  /* If possible            */
    HOLD_REQUIRED               /* Always to holding disk */
} dump_holdingdisk_t;

/* Compression types */
typedef enum {
    COMP_NONE,          /* No compression */
    COMP_FAST,          /* Fast compression on client */
    COMP_BEST,          /* Best compression on client */
    COMP_CUST,          /* Custom compression on client */
    COMP_SERVER_FAST,   /* Fast compression on server */
    COMP_SERVER_BEST,   /* Best compression on server */
    COMP_SERVER_CUST    /* Custom compression on server */
} comp_t;

/* Encryption types */
typedef enum {
    ENCRYPT_NONE,               /* No encryption */
    ENCRYPT_CUST,               /* Custom encryption on client */
    ENCRYPT_SERV_CUST           /* Custom encryption on server */
} encrypt_t;

/* Estimate strategies */
typedef enum {
    ES_CLIENT,          /* client estimate */
    ES_SERVER,          /* server estimate */
    ES_CALCSIZE,        /* calcsize estimate */
    ES_ES               /* sentinel */
} estimate_t;
/* A GSlist where each element is a element_t */
typedef GSList *estimatelist_t;

typedef enum {
    AL_OTHER_CONFIG = 1<<0,
    AL_NON_AMANDA   = 1<<1,
    AL_VOLUME_ERROR = 1<<2,
    AL_EMPTY        = 1<<3,
} autolabel_enum_t;
typedef int autolabel_set_t;

typedef struct autolabel_s {
    char            *template;
    autolabel_set_t  autolabel;
} autolabel_t;

typedef struct labelstr_s {
    char     *template;
    gboolean  match_autolabel;
} labelstr_s;

/* Dump strategies */
typedef enum {
    DS_SKIP,        /* Don't do any dumps at all */
    DS_STANDARD,    /* Standard (0 1 1 1 1 2 2 2 ...) */
    DS_NOFULL,      /* No full's (1 1 1 ...) */
    DS_NOINC,       /* No inc's (0 0 0 ...) */
    DS_4,           /* ? (0 1 2 3 4 5 6 7 8 9 10 11 ...) */
    DS_5,           /* ? (0 1 1 1 1 1 1 1 1 1 1 1 ...) */
    DS_HANOI,       /* Tower of Hanoi (? ? ? ? ? ...) */
    DS_INCRONLY,    /* Forced fulls (0 1 1 2 2 FORCE0 1 1 ...) */
    DS_DS /* sentinel */
} strategy_t;

typedef enum {
    ALGO_FIRST,
    ALGO_FIRSTFIT,
    ALGO_LARGEST,
    ALGO_LARGESTFIT,
    ALGO_SMALLEST,
    ALGO_SMALLESTFIT,	/* for internal use */
    ALGO_LAST,
    ALGO_LASTFIT,	/* for internal use */
    ALGO_ALGO /* sentinel */
} taperalgo_t;

/* execute_on types */
#define EXECUTE_ON_PRE_AMCHECK         (1 << 0)
#define EXECUTE_ON_PRE_DLE_AMCHECK     (1 << 1)
#define EXECUTE_ON_PRE_HOST_AMCHECK    (1 << 2)
#define EXECUTE_ON_POST_AMCHECK        (1 << 3)
#define EXECUTE_ON_POST_DLE_AMCHECK    (1 << 4)
#define EXECUTE_ON_POST_HOST_AMCHECK   (1 << 5)
#define EXECUTE_ON_PRE_ESTIMATE        (1 << 6)
#define EXECUTE_ON_PRE_DLE_ESTIMATE    (1 << 7)
#define EXECUTE_ON_PRE_HOST_ESTIMATE   (1 << 8)
#define EXECUTE_ON_POST_ESTIMATE       (1 << 9)
#define EXECUTE_ON_POST_DLE_ESTIMATE   (1 << 10)
#define EXECUTE_ON_POST_HOST_ESTIMATE  (1 << 11)
#define EXECUTE_ON_PRE_BACKUP          (1 << 12)
#define EXECUTE_ON_PRE_DLE_BACKUP      (1 << 13)
#define EXECUTE_ON_PRE_HOST_BACKUP     (1 << 14)
#define EXECUTE_ON_POST_BACKUP         (1 << 15)
#define EXECUTE_ON_POST_DLE_BACKUP     (1 << 16)
#define EXECUTE_ON_POST_HOST_BACKUP    (1 << 17)
#define EXECUTE_ON_PRE_RECOVER         (1 << 18)
#define EXECUTE_ON_POST_RECOVER        (1 << 19)
#define EXECUTE_ON_PRE_LEVEL_RECOVER   (1 << 20)
#define EXECUTE_ON_POST_LEVEL_RECOVER  (1 << 21)
#define EXECUTE_ON_INTER_LEVEL_RECOVER (1 << 22)
typedef int execute_on_t;

typedef enum {
    EXECUTE_WHERE_CLIENT,
    EXECUTE_WHERE_SERVER,
} execute_where_t;

typedef enum {
    SEND_AMREPORT_ALL,
    SEND_AMREPORT_STRANGE,
    SEND_AMREPORT_ERROR,
    SEND_AMREPORT_NEVER
} send_amreport_t;

typedef enum {
    DATA_PATH_AMANDA    = 1<<0,
    DATA_PATH_DIRECTTCP = 1<<1,
} data_path_t;

typedef struct exinclude_s {
    am_sl_t *sl_list;
    am_sl_t *sl_file;
    int  optional;
} exinclude_t;

typedef struct {
    int append;
    int visible;
    int priority;
    GSList* values;
    seen_t seen;
} property_t;

typedef GHashTable* proplist_t;
/* A GSlist where each element is a 'char*' */
typedef GSList* identlist_t;

typedef enum {
    TAG_NAME,
    TAG_ALL,
    TAG_OTHER
} dumptype_tag_t;

typedef enum {
    LEVEL_ALL,
    LEVEL_FULL,
    LEVEL_INCR
} level_type_t;

typedef struct {
    dumptype_tag_t  tag_type;
    char           *tag;
    level_type_t    level;
} dump_selection_t;
typedef GSList* dump_selection_list_t;

typedef struct {
    char *storage;
    int  days;
} vault_el_t;
typedef GSList* vault_list_t;

/* part_cache_types */
typedef enum {
    PART_CACHE_TYPE_NONE,
    PART_CACHE_TYPE_MEMORY,
    PART_CACHE_TYPE_DISK,
} part_cache_type_t;

/* host_limit */
typedef struct {
    gboolean server;
    gboolean same_host;
    GSList *match_pats;
} host_limit_t;

/* Names for the type of value in a val_t.  Mostly for internal use, but useful
 * for wrapping val_t's, too. */
typedef enum {
    CONFTYPE_INT,
    CONFTYPE_INT64,
    CONFTYPE_REAL,
    CONFTYPE_STR,
    CONFTYPE_IDENT,
    CONFTYPE_TIME,
    CONFTYPE_SIZE,
    CONFTYPE_BOOLEAN,
    CONFTYPE_COMPRESS,
    CONFTYPE_ENCRYPT,
    CONFTYPE_HOLDING,
    CONFTYPE_ESTIMATELIST,
    CONFTYPE_STRATEGY,
    CONFTYPE_TAPERALGO,
    CONFTYPE_PRIORITY,
    CONFTYPE_RATE,
    CONFTYPE_INTRANGE,
    CONFTYPE_EXINCLUDE,
    CONFTYPE_PROPLIST,
    CONFTYPE_APPLICATION,
    CONFTYPE_EXECUTE_ON,
    CONFTYPE_EXECUTE_WHERE,
    CONFTYPE_SEND_AMREPORT_ON,
    CONFTYPE_IDENTLIST,
    CONFTYPE_DATA_PATH,
    CONFTYPE_AUTOLABEL,
    CONFTYPE_LABELSTR,
    CONFTYPE_PART_CACHE_TYPE,
    CONFTYPE_HOST_LIMIT,
    CONFTYPE_NO_YES_ALL,
    CONFTYPE_STR_LIST,
    CONFTYPE_DUMP_SELECTION,
    CONFTYPE_VAULT_LIST,
} conftype_t;

typedef enum {
    CONF_UNIT_NONE,
    CONF_UNIT_K,
} confunit_t;

/* This should be considered an opaque type for any other modules.  The complete
 * struct is included here to allow quick access via macros. Access it *only* through
 * those macros. */
typedef struct val_s {
    union {
        int		i;
        gint64		int64;
        double		r;
        char		*s;
        size_t		size;
        time_t		t;
        float		rate[2];
        exinclude_t	exinclude;
        int		intrange[2];
        proplist_t      proplist;
	estimatelist_t  estimatelist;
	identlist_t     identlist;
        autolabel_t     autolabel;
        labelstr_s      labelstr;
	host_limit_t    host_limit;
	dump_selection_list_t   dump_selection;
	vault_list_t    vault_list;
    } v;
    seen_t seen;
    conftype_t type;
    confunit_t unit;
} val_t;

/* Functions to typecheck and extract a particular type of
 * value from a val_t.  All call error() if the type is incorrect,
 * as this is a programming error.  */
int                   val_t_to_int      (val_t *);
gint64                val_t_to_int64    (val_t *);
float                 val_t_to_real     (val_t *);
char                 *val_t_to_str      (val_t *); /* (also converts CONFTYPE_IDENT) */
char                 *val_t_to_ident    (val_t *); /* (also converts CONFTYPE_STR) */
identlist_t           val_t_to_identlist(val_t *);
identlist_t           val_t_to_str_list (val_t *);
time_t                val_t_to_time     (val_t *);
ssize_t               val_t_to_size     (val_t *);
int                   val_t_to_boolean  (val_t *);
int                   val_t_to_no_yes_all(val_t *);
comp_t                val_t_to_compress (val_t *);
encrypt_t             val_t_to_encrypt  (val_t *);
dump_holdingdisk_t    val_t_to_holding  (val_t *);
estimatelist_t        val_t_to_estimatelist (val_t *);
strategy_t            val_t_to_strategy (val_t *);
taperalgo_t           val_t_to_taperalgo(val_t *);
int                   val_t_to_priority (val_t *); /* priority_t or int */
float                *val_t_to_rate     (val_t *); /* array of two floats */
exinclude_t           val_t_to_exinclude(val_t *);
int                  *val_t_to_intrange (val_t *); /* array of two ints */
proplist_t            val_t_to_proplist (val_t *);
char                 *val_t_to_application(val_t *);
execute_on_t          val_t_to_execute_on(val_t *);
execute_where_t       val_t_to_execute_where(val_t *);
send_amreport_t       val_t_to_send_amreport(val_t *);
data_path_t           val_t_to_data_path(val_t *);
autolabel_t          *val_t_to_autolabel(val_t *);
labelstr_s           *val_t_to_labelstr(val_t *);
part_cache_type_t     val_t_to_part_cache_type(val_t *);
host_limit_t         *val_t_to_host_limit(val_t *);
dump_selection_list_t val_t_to_dump_selection(val_t *);
vault_list_t          val_t_to_vault_list(val_t *);

/* Has the given val_t been seen in a configuration file or config overwrite?
 *
 * @param val: val_t* to examine
 * @returns: boolean
 */
#define val_t_seen(val) ((val)->seen.linenum)

/* What is the underlying type of this val_t?
 *
 * @param val: val_t* to examine
 * @returns: conftype_t
 */
#define val_t_type(val) ((val)->type)

/* Macros to convert val_t's to a particular type without the benefit of
 * a typecheck.  Use these only if you really know what you're doing!
 *
 * Implementation note: these macros encode the relationship of conftypes
 * (in the macro name) to the corresponding union field.  The macros work
 * as lvalues, too.
 */
#define val_t__seen(val)            ((val)->seen)
#define val_t__int(val)             ((val)->v.i)
#define val_t__int64(val)           ((val)->v.int64)
#define val_t__real(val)            ((val)->v.r)
#define val_t__str(val)             ((val)->v.s)
#define val_t__ident(val)           ((val)->v.s)
#define val_t__identlist(val)       ((val)->v.identlist)
#define val_t__time(val)            ((val)->v.t)
#define val_t__size(val)            ((val)->v.size)
#define val_t__boolean(val)         ((val)->v.i)
#define val_t__no_yes_all(val)      ((val)->v.i)
#define val_t__compress(val)        ((val)->v.i)
#define val_t__encrypt(val)         ((val)->v.i)
#define val_t__holding(val)         ((val)->v.i)
#define val_t__estimatelist(val)    ((val)->v.estimatelist)
#define val_t__strategy(val)        ((val)->v.i)
#define val_t__taperalgo(val)       ((val)->v.i)
#define val_t__send_amreport(val)   ((val)->v.i)
#define val_t__priority(val)        ((val)->v.i)
#define val_t__rate(val)            ((val)->v.rate)
#define val_t__exinclude(val)       ((val)->v.exinclude)
#define val_t__intrange(val)        ((val)->v.intrange)
#define val_t__proplist(val)        ((val)->v.proplist)
#define val_t__application(val)     ((val)->v.application)
#define val_t__execute_on(val)      ((val)->v.i)
#define val_t__execute_where(val)   ((val)->v.i)
#define val_t__data_path(val)       ((val)->v.i)
#define val_t__autolabel(val)       (&((val)->v.autolabel))
#define val_t__labelstr(val)        (&((val)->v.labelstr))
#define val_t__part_cache_type(val) ((val)->v.i)
#define val_t__host_limit(val)      ((val)->v.host_limit)
#define val_t__dump_selection(val)  ((val)->v.dump_selection)
#define val_t__vault_list(val)      ((val)->v.vault_list)

/*
 * Parameters
 *
 * Programs get val_t's by giving the index of the parameters they're interested in.
 * For global parameters, these start with CNF; for subsections, they start with the
 * name of the subsection.
 */

/*
 * Global parameter access
 */
typedef enum {
    CNF_ORG,
    CNF_CONF,
    CNF_INDEX_SERVER,
    CNF_TAPE_SERVER,
    CNF_AMDUMP_SERVER,
    CNF_AUTH,
    CNF_SSH_KEYS,
    CNF_AMANDAD_PATH,
    CNF_CLIENT_USERNAME,
    CNF_CLIENT_PORT,
    CNF_GNUTAR_LIST_DIR,
    CNF_AMANDATES,
    CNF_MAILTO,
    CNF_DUMPUSER,
    CNF_TAPEDEV,
    CNF_DEVICE_PROPERTY,
    CNF_PROPERTY,
    CNF_INTERACTIVITY,
    CNF_APPLICATION,
    CNF_APPLICATION_TOOL,
    CNF_EXECUTE_ON,
    CNF_PP_SCRIPT,
    CNF_PP_SCRIPT_TOOL,
    CNF_PLUGIN,
    CNF_CHANGERDEV,
    CNF_CHANGERFILE,
    CNF_LABELSTR,
    CNF_TAPELIST,
    CNF_DISKFILE,
    CNF_INFOFILE,
    CNF_LOGDIR,
    CNF_INDEXDIR,
    CNF_TAPETYPE,
    CNF_CATALOG,
    CNF_DUMPCYCLE,
    CNF_RUNSPERCYCLE,
    CNF_TAPECYCLE,
    CNF_NETUSAGE,
    CNF_INPARALLEL,
    CNF_DUMPORDER,
    CNF_BUMPPERCENT,
    CNF_BUMPSIZE,
    CNF_BUMPMULT,
    CNF_BUMPDAYS,
    CNF_TPCHANGER,
    CNF_RUNTAPES,
    CNF_MAXDUMPS,
    CNF_ETIMEOUT,
    CNF_DTIMEOUT,
    CNF_CTIMEOUT,
    CNF_DEVICE_OUTPUT_BUFFER_SIZE,
    CNF_PRINTER,
    CNF_MAILER,
    CNF_AUTOFLUSH,
    CNF_RESERVE,
    CNF_MAXDUMPSIZE,
    CNF_COLUMNSPEC,
    CNF_AMRECOVER_DO_FSF,
    CNF_AMRECOVER_CHECK_LABEL,
    CNF_AMRECOVER_CHANGER,
    CNF_TAPERALGO,
    CNF_SEND_AMREPORT_ON,
    CNF_FLUSH_THRESHOLD_DUMPED,
    CNF_FLUSH_THRESHOLD_SCHEDULED,
    CNF_TAPERFLUSH,
    CNF_DISPLAYUNIT,
    CNF_KRB5KEYTAB,
    CNF_KRB5PRINCIPAL,
    CNF_LABEL_NEW_TAPES,
    CNF_USETIMESTAMPS,
    CNF_REP_TRIES,
    CNF_CONNECT_TRIES,
    CNF_REQ_TRIES,
    CNF_DEBUG_AMANDAD,
    CNF_DEBUG_RECOVERY,
    CNF_DEBUG_AMIDXTAPED,
    CNF_DEBUG_AMINDEXD,
    CNF_DEBUG_AMRECOVER,
    CNF_DEBUG_AUTH,
    CNF_DEBUG_EVENT,
    CNF_DEBUG_HOLDING,
    CNF_DEBUG_PROTOCOL,
    CNF_DEBUG_PLANNER,
    CNF_DEBUG_DRIVER,
    CNF_DEBUG_DUMPER,
    CNF_DEBUG_CHUNKER,
    CNF_DEBUG_TAPER,
    CNF_DEBUG_SELFCHECK,
    CNF_DEBUG_SENDSIZE,
    CNF_DEBUG_SENDBACKUP,
    CNF_RESERVED_UDP_PORT,
    CNF_RESERVED_TCP_PORT,
    CNF_UNRESERVED_TCP_PORT,
    CNF_HOLDINGDISK,
    CNF_AUTOLABEL,
    CNF_META_AUTOLABEL,
    CNF_DEBUG_DAYS,
    CNF_TAPER_PARALLEL_WRITE,
    CNF_RECOVERY_LIMIT,
    CNF_TAPERSCAN,
    CNF_MAX_DLE_BY_VOLUME,
    CNF_EJECT_VOLUME,
    CNF_TMPDIR,
    CNF_REPORT_USE_MEDIA,
    CNF_REPORT_NEXT_MEDIA,
    CNF_REPORT_FORMAT,
    CNF_ACTIVE_STORAGE,
    CNF_STORAGE,
    CNF_VAULT_STORAGE,
    CNF_CMDFILE,
    CNF_COMPRESS_INDEX,
    CNF_SORT_INDEX,
    CNF_REST_API_PORT,
    CNF_REST_SSL_CERT,
    CNF_REST_SSL_KEY,
    CNF_SSL_FINGERPRINT_FILE,
    CNF_SSL_CERT_FILE,
    CNF_SSL_KEY_FILE,
    CNF_SSL_CA_CERT_FILE,
    CNF_SSL_CIPHER_LIST,
    CNF_SSL_CHECK_HOST,
    CNF_SSL_CHECK_CERTIFICATE_HOST,
    CNF_SSL_DIR,
    CNF_SSL_CHECK_FINGERPRINT,
    CNF_HOSTNAME,
    CNF_CNF /* sentinel */
} confparm_key;

/* Given a confparm_key, return a pointer to the corresponding val_t.
 *
 * @param key: confparm_key
 * @returns: pointer to value
 */
val_t *getconf(confparm_key key);

/* (convenience macro) has this global parameter been seen?
 *
 * @param key: confparm_key
 * @returns: boolean
 */
#define getconf_seen(key)       (val_t_seen(getconf((key))))
#define getconf_linenum(key)       (val_t_seen(getconf((key))))

/* (convenience macros)
 * Fetch a global parameter of a specific type.  Note that these
 * convenience macros have a different form from those for the
 * subsections: here you specify a type and a key, while for the
 * subsections you specify only a key.  The difference is historical.
 *
 * @param key: confparm_key
 * @returns: various
 */
#define getconf_int(key)          (val_t_to_int(getconf((key))))
#define getconf_int64(key)        (val_t_to_int64(getconf((key))))
#define getconf_real(key)         (val_t_to_real(getconf((key))))
#define getconf_str(key)	  (val_t_to_str(getconf((key))))
#define getconf_ident(key)        (val_t_to_ident(getconf((key))))
#define getconf_identlist(key)    (val_t_to_identlist(getconf((key))))
#define getconf_time(key)         (val_t_to_time(getconf((key))))
#define getconf_size(key)         (val_t_to_size(getconf((key))))
#define getconf_boolean(key)      (val_t_to_boolean(getconf((key))))
#define getconf_no_yes_all(key)   (val_t_to_no_yes_all(getconf((key))))
#define getconf_compress(key)     (val_t_to_compress(getconf((key))))
#define getconf_encrypt(key)      (val_t_to_encrypt(getconf((key))))
#define getconf_holding(key)      (val_t_to_holding(getconf((key))))
#define getconf_estimatelist(key) (val_t_to_estimatelist(getconf((key))))
#define getconf_strategy(key)     (val_t_to_strategy(getconf((key))))
#define getconf_taperalgo(key)    (val_t_to_taperalgo(getconf((key))))
#define getconf_priority(key)     (val_t_to_priority(getconf((key))))
#define getconf_rate(key)         (val_t_to_rate(getconf((key))))
#define getconf_exinclude(key)    (val_t_to_exinclude(getconf((key))))
#define getconf_intrange(key)     (val_t_to_intrange(getconf((key))))
#define getconf_proplist(key)     (val_t_to_proplist(getconf((key))))
#define getconf_send_amreport(key) (val_t_to_send_amreport(getconf((key))))
#define getconf_autolabel(key)    (val_t_to_autolabel(getconf((key))))
#define getconf_labelstr(key)     (val_t_to_labelstr(getconf((key))))
#define getconf_part_cache_type(key) (val_t_to_part_cache_type(getconf((key))))
#define getconf_recovery_limit(key) (val_t_to_host_limit(getconf((key))))

/* Get a list of names for subsections of the given type
 *
 * @param listname: the desired type of subsection
 * @returns: list of subsection names; caller is responsible for freeing
 * this list, but not the strings it points to, using g_slist_free().
 */
GSList *getconf_list(char *listname);

/* Get a configuration value by name, supporting the TYPE:SUBSEC:KEYWORD.
 * Returns NULL if the configuration value doesnt exist.
 */
val_t *getconf_byname(char *key);

char *confparm_key_to_name(int parm);

/*
 * Derived values
 *
 * Values which aren't directly specified by the configuration, but which
 * are derived from it.
 */

/* Return a divisor which will convert a value in units of kilo-whatevers
 * to the user's selected display unit.
 *
 * @returns: long integer divisor
 */
long int getconf_unit_divisor(void);

extern char *prepend_prefix;

/* If any of these globals are true, the corresponding component will
 * send verbose debugging output to the debug file.  The options are
 * set during config_init, but can be modified at will after that if 
 * desired.  */

extern int debug_amandad;
extern int debug_recovery;
extern int debug_amidxtaped;
extern int debug_amindexd;
extern int debug_amrecover;
extern int debug_auth;
extern int debug_event;
extern int debug_holding;
extern int debug_protocol;
extern int debug_planner;
extern int debug_driver;
extern int debug_dumper;
extern int debug_chunker;
extern int debug_taper;
extern int debug_selfcheck;
extern int debug_sendsize;
extern int debug_sendbackup;

/*
 * Tapetype parameter access
 */

typedef enum {
    TAPETYPE_COMMENT,
    TAPETYPE_LBL_TEMPL,
    TAPETYPE_BLOCKSIZE,
    TAPETYPE_READBLOCKSIZE,
    TAPETYPE_LENGTH,
    TAPETYPE_FILEMARK,
    TAPETYPE_SPEED,
    TAPETYPE_PART_SIZE,
    TAPETYPE_PART_CACHE_TYPE,
    TAPETYPE_PART_CACHE_DIR,
    TAPETYPE_PART_CACHE_MAX_SIZE,
    TAPETYPE_TAPETYPE /* sentinel */
} tapetype_key;

/* opaque object */
typedef struct tapetype_s tapetype_t;

/* Given the name of the tapetype, return a tapetype object.  Returns NULL
 * if no matching tapetype exists.  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired tapetype
 * @returns: object or NULL
 */
tapetype_t *lookup_tapetype(char *identifier);

/* Given a tapetype and a key, return a pointer to the corresponding val_t.
 *
 * @param ttyp: the tapetype to examine
 * @param key: tapetype_key (one of the TAPETYPE_* constants)
 * @returns: pointer to value
 */
val_t *tapetype_getconf(tapetype_t *ttyp, tapetype_key key);

/* Get the name of this tapetype.
 *
 * @param ttyp: the tapetype to examine
 * @returns: name of the tapetype
 */
char *tapetype_name(tapetype_t *ttyp);

char *tapetype_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this tapetype?  This
 * applies to the specific parameter *within* the tapetype.
 *
 * @param key: tapetype_key
 * @returns: boolean
 */
#define tapetype_seen(ttyp, key)       (val_t_seen(tapetype_getconf((ttyp), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param ttyp: the tapetype to examine
 * @returns: various
 */
#define tapetype_get_comment(ttyp)             (val_t_to_str(tapetype_getconf((ttyp), TAPETYPE_COMMENT)))
#define tapetype_get_lbl_templ(ttyp)           (val_t_to_str(tapetype_getconf((ttyp), TAPETYPE_LBL_TEMPL)))
#define tapetype_get_blocksize(ttyp)           (val_t_to_size(tapetype_getconf((ttyp), TAPETYPE_BLOCKSIZE)))
#define tapetype_get_readblocksize(ttyp)       (val_t_to_size(tapetype_getconf((ttyp), TAPETYPE_READBLOCKSIZE)))
#define tapetype_get_length(ttyp)              (val_t_to_int64(tapetype_getconf((ttyp), TAPETYPE_LENGTH)))
#define tapetype_get_filemark(ttyp)            (val_t_to_int64(tapetype_getconf((ttyp), TAPETYPE_FILEMARK)))
#define tapetype_get_speed(ttyp)               (val_t_to_int(tapetype_getconf((ttyp), TAPETYPE_SPEED)))
#define tapetype_get_part_size(ttyp)           (val_t_to_int64(tapetype_getconf((ttyp), TAPETYPE_PART_SIZE)))
#define tapetype_get_part_cache_type(ttyp)     (val_t_to_part_cache_type(tapetype_getconf((ttyp), TAPETYPE_PART_CACHE_TYPE)))
#define tapetype_get_part_cache_dir(ttyp)      (val_t_to_str(tapetype_getconf((ttyp), TAPETYPE_PART_CACHE_DIR)))
#define tapetype_get_part_cache_max_size(ttyp) (val_t_to_int64(tapetype_getconf((ttyp), TAPETYPE_PART_CACHE_MAX_SIZE)))

/*
 * Dumptype parameter access
 *
 * Note that some parameters are generic to the host
 */

typedef enum {
    DUMPTYPE_COMMENT,
    DUMPTYPE_PROGRAM,
    DUMPTYPE_SRVCOMPPROG,
    DUMPTYPE_CLNTCOMPPROG,
    DUMPTYPE_SRV_ENCRYPT,
    DUMPTYPE_CLNT_ENCRYPT,
    DUMPTYPE_AMANDAD_PATH,		/* host parameter */
    DUMPTYPE_CLIENT_USERNAME,		/* host parameter */
    DUMPTYPE_SSH_KEYS,			/* host parameter */
    DUMPTYPE_AUTH,			/* host parameter */
    DUMPTYPE_EXCLUDE,
    DUMPTYPE_INCLUDE,
    DUMPTYPE_PRIORITY,
    DUMPTYPE_DUMPCYCLE,
    DUMPTYPE_MAXDUMPS,		        /* host parameter */
    DUMPTYPE_MAXPROMOTEDAY,
    DUMPTYPE_BUMPPERCENT,
    DUMPTYPE_BUMPSIZE,
    DUMPTYPE_BUMPDAYS,
    DUMPTYPE_BUMPMULT,
    DUMPTYPE_STARTTIME,
    DUMPTYPE_STRATEGY,
    DUMPTYPE_ESTIMATELIST,
    DUMPTYPE_COMPRESS,
    DUMPTYPE_ENCRYPT,
    DUMPTYPE_SRV_DECRYPT_OPT,
    DUMPTYPE_CLNT_DECRYPT_OPT,
    DUMPTYPE_COMPRATE,
    DUMPTYPE_TAPE_SPLITSIZE,
    DUMPTYPE_FALLBACK_SPLITSIZE,
    DUMPTYPE_SPLIT_DISKBUFFER,
    DUMPTYPE_RECORD,
    DUMPTYPE_SKIP_INCR,
    DUMPTYPE_SKIP_FULL,
    DUMPTYPE_HOLDINGDISK,
    DUMPTYPE_KENCRYPT,
    DUMPTYPE_IGNORE,
    DUMPTYPE_INDEX,
    DUMPTYPE_APPLICATION,
    DUMPTYPE_SCRIPTLIST,
    DUMPTYPE_PROPERTY,
    DUMPTYPE_CLIENT_PORT,
    DUMPTYPE_DATA_PATH,
    DUMPTYPE_ALLOW_SPLIT,
    DUMPTYPE_RECOVERY_LIMIT,
    DUMPTYPE_DUMP_LIMIT,
    DUMPTYPE_MAX_WARNINGS,
    DUMPTYPE_RETRY_DUMP,
    DUMPTYPE_TAG,
    DUMPTYPE_SSL_FINGERPRINT_FILE,
    DUMPTYPE_SSL_CERT_FILE,
    DUMPTYPE_SSL_KEY_FILE,
    DUMPTYPE_SSL_CA_CERT_FILE,
    DUMPTYPE_SSL_CIPHER_LIST,
    DUMPTYPE_SSL_CHECK_HOST,
    DUMPTYPE_SSL_CHECK_CERTIFICATE_HOST,
    DUMPTYPE_SSL_CHECK_FINGERPRINT,
    DUMPTYPE_DUMPTYPE /* sentinel */
} dumptype_key;

/* opaque object */
typedef struct dumptype_s dumptype_t;

/* Given the name of the dumptype, return a dumptype object.  Returns NULL
 * if no matching dumptype exists.  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired dumptype
 * @returns: object or NULL
 */
dumptype_t *lookup_dumptype(char *identifier);

/* Given a dumptype and a key, return a pointer to the corresponding val_t.
 *
 * @param dtyp: the dumptype to examine
 * @param key: dumptype_key (one of the TAPETYPE_* constants)
 * @returns: pointer to value
 */
val_t *dumptype_getconf(dumptype_t *dtyp, dumptype_key key);

/* Get the name of this dumptype.
 *
 * @param dtyp: the dumptype to examine
 * @returns: name of the dumptype
 */
char *dumptype_name(dumptype_t *dtyp);

char *dumptype_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this dumptype?  This
 * applies to the specific parameter *within* the dumptype.
 *
 * @param key: dumptype_key
 * @returns: boolean
 */
#define dumptype_seen(dtyp, key)       (val_t_seen(dumptype_getconf((dtyp), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param dtyp: the dumptype to examine
 * @returns: various
 */
#define dumptype_get_comment(dtyp)             (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_COMMENT)))
#define dumptype_get_program(dtyp)             (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_PROGRAM)))
#define dumptype_get_srvcompprog(dtyp)         (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_SRVCOMPPROG)))
#define dumptype_get_clntcompprog(dtyp)        (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_CLNTCOMPPROG)))
#define dumptype_get_srv_encrypt(dtyp)         (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_SRV_ENCRYPT)))
#define dumptype_get_clnt_encrypt(dtyp)        (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_CLNT_ENCRYPT)))
#define dumptype_get_amandad_path(dtyp)        (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_AMANDAD_PATH)))
#define dumptype_get_client_username(dtyp)     (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_CLIENT_USERNAME)))
#define dumptype_get_ssh_keys(dtyp)            (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_SSH_KEYS)))
#define dumptype_get_auth(dtyp)                (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_AUTH)))
#define dumptype_get_exclude(dtyp)             (val_t_to_exinclude(dumptype_getconf((dtyp), DUMPTYPE_EXCLUDE)))
#define dumptype_get_include(dtyp)             (val_t_to_exinclude(dumptype_getconf((dtyp), DUMPTYPE_INCLUDE)))
#define dumptype_get_priority(dtyp)            (val_t_to_priority(dumptype_getconf((dtyp), DUMPTYPE_PRIORITY)))
#define dumptype_get_dumpcycle(dtyp)           (val_t_to_int(dumptype_getconf((dtyp), DUMPTYPE_DUMPCYCLE)))
#define dumptype_get_maxcycle(dtyp)            (val_t_to_int(dumptype_getconf((dtyp), DUMPTYPE_MAXCYCLE)))
#define dumptype_get_frequency(dtyp)           (val_t_to_int(dumptype_getconf((dtyp), DUMPTYPE_FREQUENCY)))
#define dumptype_get_maxdumps(dtyp)            (val_t_to_int(dumptype_getconf((dtyp), DUMPTYPE_MAXDUMPS)))
#define dumptype_get_maxpromoteday(dtyp)       (val_t_to_int(dumptype_getconf((dtyp), DUMPTYPE_MAXPROMOTEDAY)))
#define dumptype_get_bumppercent(dtyp)         (val_t_to_int(dumptype_getconf((dtyp), DUMPTYPE_BUMPPERCENT)))
#define dumptype_get_bumpsize(dtyp)            (val_t_to_int64(dumptype_getconf((dtyp), DUMPTYPE_BUMPSIZE)))
#define dumptype_get_bumpdays(dtyp)            (val_t_to_int(dumptype_getconf((dtyp), DUMPTYPE_BUMPDAYS)))
#define dumptype_get_bumpmult(dtyp)            (val_t_to_real(dumptype_getconf((dtyp), DUMPTYPE_BUMPMULT)))
#define dumptype_get_starttime(dtyp)           (val_t_to_time(dumptype_getconf((dtyp), DUMPTYPE_STARTTIME)))
#define dumptype_get_strategy(dtyp)            (val_t_to_strategy(dumptype_getconf((dtyp), DUMPTYPE_STRATEGY)))
#define dumptype_get_estimatelist(dtyp)        (val_t_to_estimatelist(dumptype_getconf((dtyp), DUMPTYPE_ESTIMATELIST)))
#define dumptype_get_compress(dtyp)            (val_t_to_compress(dumptype_getconf((dtyp), DUMPTYPE_COMPRESS)))
#define dumptype_get_encrypt(dtyp)             (val_t_to_encrypt(dumptype_getconf((dtyp), DUMPTYPE_ENCRYPT)))
#define dumptype_get_srv_decrypt_opt(dtyp)     (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_SRV_DECRYPT_OPT)))
#define dumptype_get_clnt_decrypt_opt(dtyp)    (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_CLNT_DECRYPT_OPT)))
#define dumptype_get_comprate(dtyp)            (val_t_to_rate(dumptype_getconf((dtyp), DUMPTYPE_COMPRATE)))
#define dumptype_get_tape_splitsize(dtyp)      (val_t_to_int64(dumptype_getconf((dtyp), DUMPTYPE_TAPE_SPLITSIZE)))
#define dumptype_get_fallback_splitsize(dtyp)  (val_t_to_int64(dumptype_getconf((dtyp), DUMPTYPE_FALLBACK_SPLITSIZE)))
#define dumptype_get_split_diskbuffer(dtyp)    (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_SPLIT_DISKBUFFER)))
#define dumptype_get_record(dtyp)              (val_t_to_boolean(dumptype_getconf((dtyp), DUMPTYPE_RECORD)))
#define dumptype_get_skip_incr(dtyp)           (val_t_to_boolean(dumptype_getconf((dtyp), DUMPTYPE_SKIP_INCR)))
#define dumptype_get_skip_full(dtyp)           (val_t_to_boolean(dumptype_getconf((dtyp), DUMPTYPE_SKIP_FULL)))
#define dumptype_get_to_holdingdisk(dtyp)      (val_t_to_holding(dumptype_getconf((dtyp), DUMPTYPE_HOLDINGDISK)))
#define dumptype_get_kencrypt(dtyp)            (val_t_to_boolean(dumptype_getconf((dtyp), DUMPTYPE_KENCRYPT)))
#define dumptype_get_ignore(dtyp)              (val_t_to_boolean(dumptype_getconf((dtyp), DUMPTYPE_IGNORE)))
#define dumptype_get_index(dtyp)               (val_t_to_boolean(dumptype_getconf((dtyp), DUMPTYPE_INDEX)))
#define dumptype_get_application(dtyp)         (val_t_to_application(dumptype_getconf((dtyp), DUMPTYPE_APPLICATION)))
#define dumptype_get_scriptlist(dtyp)          (val_t_to_identlist(dumptype_getconf((dtyp), DUMPTYPE_SCRIPTLIST)))
#define dumptype_get_property(dtyp)            (val_t_to_proplist(dumptype_getconf((dtyp), DUMPTYPE_PROPERTY)))
#define dumptype_get_client_port(dtyp)         (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_CLIENT_PORT)))
#define dumptype_get_data_path(dtyp)           (val_t_to_data_path(dumptype_getconf((dtyp), DUMPTYPE_DATA_PATH)))
#define dumptype_get_allow_split(dtyp)         (val_t_to_boolean(dumptype_getconf((dtyp), DUMPTYPE_ALLOW_SPLIT)))
#define dumptype_get_recovery_limit(dtyp)      (val_t_to_host_limit(dumptype_getconf((dtyp), DUMPTYPE_RECOVERY_LIMIT)))
#define dumptype_get_dump_limit(dtyp)          (val_t_to_host_limit(dumptype_getconf((dtyp), DUMPTYPE_DUMP_LIMIT)))
#define dumptype_get_max_warnings(dtyp)        (val_t_to_int(dumptype_getconf((dtyp), DUMPTYPE_MAX_WARNINGS)))
#define dumptype_get_retry_dump(dtyp)          (val_t_to_int(dumptype_getconf((dtyp), DUMPTYPE_RETRY_DUMP)))
#define dumptype_get_tags(dtyp)                (val_t_to_str_list(dumptype_getconf((dtyp), DUMPTYPE_TAG)))
#define dumptype_get_ssl_fingerprint_file(dtyp) (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_SSL_FINGERPRINT_FILE)))
#define dumptype_get_ssl_cert_file(dtyp)        (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_SSL_CERT_FILE)))
#define dumptype_get_ssl_key_file(dtyp)         (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_SSL_KEY_FILE)))
#define dumptype_get_ssl_ca_cert_file(dtyp)     (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_SSL_CA_CERT_FILE)))
#define dumptype_get_ssl_cipher_list(dtyp)      (val_t_to_str(dumptype_getconf((dtyp), DUMPTYPE_SSL_CIPHER_LIST)))
#define dumptype_get_ssl_check_host(dtyp)       (val_t_to_boolean(dumptype_getconf((dtyp), DUMPTYPE_SSL_CHECK_HOST)))
#define dumptype_get_ssl_check_certificate_host(dtyp) (val_t_to_boolean(dumptype_getconf((dtyp), DUMPTYPE_SSL_CHECK_CERTIFICATE_HOST)))
#define dumptype_get_ssl_check_fingerprint(dtyp)(val_t_to_boolean(dumptype_getconf((dtyp), DUMPTYPE_SSL_CHECK_FINGERPRINT)))

/*
 * Interface parameter access
 */

typedef enum {
    INTER_COMMENT,
    INTER_MAXUSAGE,
    INTER_SRC_IP,
    INTER_INTER /* sentinel */
} interface_key;

/* opaque object */
typedef struct interface_s interface_t;

/* Given the name of the interface, return a interface object.  Returns NULL
 * if no matching interface exists.  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired interface
 * @returns: object or NULL
 */
interface_t *lookup_interface(char *identifier);

/* Given a interface and a key, return a pointer to the corresponding val_t.
 *
 * @param iface: the interface to examine
 * @param key: interface_key (one of the TAPETYPE_* constants)
 * @returns: pointer to value
 */
val_t *interface_getconf(interface_t *iface, interface_key key);

/* Get the name of this interface.
 *
 * @param iface: the interface to examine
 * @returns: name of the interface
 */
char *interface_name(interface_t *iface);

char *interface_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this interface?  This
 * applies to the specific parameter *within* the interface.
 *
 * @param key: interface_key
 * @returns: boolean
 */
#define interface_seen(iface, key)       (val_t_seen(interface_getconf((iface), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param iface: the interface to examine
 * @returns: various
 */
#define interface_get_comment(iface)    (val_t_to_str(interface_getconf((iface), INTER_COMMENT)))
#define interface_get_maxusage(iface)   (val_t_to_int(interface_getconf((iface), INTER_MAXUSAGE)))
#define interface_get_src_ip(iface)     (val_t_to_str(interface_getconf((iface), INTER_SRC_IP)))

/*
 * Holdingdisk parameter access
 */

typedef enum {
    HOLDING_COMMENT,
    HOLDING_DISKDIR,
    HOLDING_DISKSIZE,
    HOLDING_CHUNKSIZE,
    HOLDING_HOLDING /* sentinel */
} holdingdisk_key;

/* opaque object */
typedef struct holdingdisk_s holdingdisk_t;

/* Given the name of the holdingdisk, return a holdingdisk object.  Returns NULL
 * if no matching holdingdisk exists.  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired holdingdisk
 * @returns: object or NULL
 */
holdingdisk_t *lookup_holdingdisk(char *identifier);

/* Return the whole linked list of holdingdisks.
 *
 * @returns: first holding disk
 */
GSList *getconf_holdingdisks(void);

/* Given a holdingdisk and a key, return a pointer to the corresponding val_t.
 *
 * @param hdisk: the holdingdisk to examine
 * @param key: holdingdisk_key (one of the TAPETYPE_* constants)
 * @returns: pointer to value
 */
val_t *holdingdisk_getconf(holdingdisk_t *hdisk, holdingdisk_key key);

/* Get the name of this holdingdisk.
 *
 * @param hdisk: the holdingdisk to examine
 * @returns: name of the holdingdisk
 */
char *holdingdisk_name(holdingdisk_t *hdisk);

char *holdingdisk_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this holdingdisk?  This
 * applies to the specific parameter *within* the holdingdisk.
 *
 * @param key: holdingdisk_key
 * @returns: boolean
 */
#define holdingdisk_seen(hdisk, key)       (val_t_seen(holdingdisk_getconf((hdisk), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param hdisk: the holdingdisk to examine
 * @returns: various
 */
#define holdingdisk_get_comment(hdisk)   (val_t_to_str(holdingdisk_getconf((hdisk), HOLDING_COMMENT)))
#define holdingdisk_get_diskdir(hdisk)   (val_t_to_str(holdingdisk_getconf((hdisk), HOLDING_DISKDIR)))
#define holdingdisk_get_disksize(hdisk)  (val_t_to_int64(holdingdisk_getconf((hdisk), HOLDING_DISKSIZE)))
#define holdingdisk_get_chunksize(hdisk) (val_t_to_int64(holdingdisk_getconf((hdisk), HOLDING_CHUNKSIZE)))

/* A application-tool interface */
typedef enum application_e  {
    APPLICATION_COMMENT,
    APPLICATION_PLUGIN,
    APPLICATION_PROPERTY,
    APPLICATION_CLIENT_NAME,
    APPLICATION_APPLICATION
} application_key;

/* opaque object */
typedef struct application_s application_t;

/* Given the name of the application, return a application object.  Returns NULL
 * if no matching application exists.  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired application
 * @returns: object or NULL
 */

application_t *lookup_application(char *identifier);

/* Given a application and a key, return a pointer to the corresponding val_t.
 *
 * @param ttyp: the application to examine
 * @param key: application (one of the APPLICATION_* constants)
 * @returns: pointer to value
 */
val_t *application_getconf(application_t *app, application_key key);

/* Get the name of this application.
 *
 * @param ttyp: the application to examine
 * @returns: name of the application
 */
char *application_name(application_t *app);

char *application_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this application?  This
 * applies to the specific parameter *within* the application.
 *
 * @param key: application_key
 * @returns: boolean
 */
#define application_seen(app, key)       (val_t_seen(application_getconf((app), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param ttyp: the application to examine
 * @returns: various
 */
#define application_get_comment(application)  (val_t_to_str(application_getconf((application), APPLICATION_COMMENT))
#define application_get_plugin(application)   (val_t_to_str(application_getconf((application), APPLICATION_PLUGIN)))
#define application_get_property(application) (val_t_to_proplist(application_getconf((application), APPLICATION_PROPERTY)))
#define application_get_client_name(application) (val_t_to_str(application_getconf((application), APPLICATION_CLIENT_NAME)))

/* A pp-script-tool interface */
typedef enum pp_script_e  {
    PP_SCRIPT_COMMENT,
    PP_SCRIPT_PLUGIN,
    PP_SCRIPT_PROPERTY,
    PP_SCRIPT_EXECUTE_ON,
    PP_SCRIPT_EXECUTE_WHERE,
    PP_SCRIPT_ORDER,
    PP_SCRIPT_SINGLE_EXECUTION,
    PP_SCRIPT_CLIENT_NAME,
    PP_SCRIPT_PP_SCRIPT
} pp_script_key;

/* opaque object */
typedef struct pp_script_s pp_script_t;

/* Given the name of the pp_script, return a pp_script object.  Returns NULL
 * if no matching pp_script exists.  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired pp_script
 * @returns: object or NULL
 */

pp_script_t *lookup_pp_script(char *identifier);

/* Given a pp_script and a key, return a pointer to the corresponding val_t.
 *
 * @param ttyp: the pp_script to examine
 * @param key: pp_script (one of the PP_SCRIPT_* constants)
 * @returns: pointer to value
 */
val_t *pp_script_getconf(pp_script_t *pps, pp_script_key key);

/* Get the name of this pp_script.
 *
 * @param ttyp: the pp_script to examine
 * @returns: name of the pp_script
 */
char *pp_script_name(pp_script_t *pps);

char *pp_script_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this pp_script?  This
 * applies to the specific parameter *within* the pp_script.
 *
 * @param key: pp_script_key
 * @returns: boolean
 */
#define pp_script_seen(pps, key)       (val_t_seen(pp_script_getconf((pps), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param ttyp: the pp_script to examine
 * @returns: various
 */

#define pp_script_get_comment(pp_script)   (val_t_to_str(pp_script_getconf((pp_script), PP_SCRIPT_COMMENT)))
#define pp_script_get_plugin(pp_script)   (val_t_to_str(pp_script_getconf((pp_script), PP_SCRIPT_PLUGIN)))
#define pp_script_get_property(pp_script)   (val_t_to_proplist(pp_script_getconf((pp_script), PP_SCRIPT_PROPERTY)))
#define pp_script_get_execute_on(pp_script)   (val_t_to_execute_on(pp_script_getconf((pp_script), PP_SCRIPT_EXECUTE_ON)))
#define pp_script_get_execute_where(pp_script)   (val_t_to_execute_where(pp_script_getconf((pp_script), PP_SCRIPT_EXECUTE_WHERE)))
#define pp_script_get_order(pp_script)   (val_t_to_int(pp_script_getconf((pp_script), PP_SCRIPT_ORDER)))
#define pp_script_get_single_execution(pp_script)   (val_t_to_boolean(pp_script_getconf((pp_script), PP_SCRIPT_SINGLE_EXECUTION)))
#define pp_script_get_client_name(pp_script)   (val_t_to_str(pp_script_getconf((pp_script), PP_SCRIPT_CLIENT_NAME)))

pp_script_t *lookup_pp_script(char *identifier);

/* A device definition */
typedef enum {
    DEVICE_CONFIG_COMMENT,
    DEVICE_CONFIG_TAPEDEV,
    DEVICE_CONFIG_DEVICE_PROPERTY,
    DEVICE_CONFIG_DEVICE_CONFIG
} device_config_key;

/* opaque object */
typedef struct device_config_s device_config_t;

/* Given the name of the device, return a device_config_t object.  Returns NULL
 * if no matching device exists.  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired device
 * @returns: object or NULL
 */

device_config_t *lookup_device_config(char *identifier);

/* Given a device_config and a key, return a pointer to the corresponding val_t.
 *
 * @param ttyp: the device_config to examine
 * @param key: device_config (one of the DEVICE_CONFIG_* constants)
 * @returns: pointer to value
 */
val_t *device_config_getconf(device_config_t *devconf, device_config_key key);

/* Get the name of this device_config.
 *
 * @param ttyp: the device_config to examine
 * @returns: name of the device_config
 */
char *device_config_name(device_config_t *devconf);

char *device_config_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this device_config?  This
 * applies to the specific parameter *within* the device_config.
 *
 * @param key: device_config_key
 * @returns: boolean
 */
#define device_config_seen(devconf, key)       (val_t_seen(device_config_getconf((devconf), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param devconf: the device_config to examine
 * @returns: various
 */

#define device_config_get_comment(devconf)   (val_t_to_str(device_config_getconf((devconf), DEVICE_CONFIG_COMMENT)))
#define device_config_get_tapedev(devconf)   (val_t_to_str(device_config_getconf((devconf), DEVICE_CONFIG_TAPEDEV)))
#define device_config_get_property(devconf)   (val_t_to_proplist(device_config_getconf((devconf), DEVICE_CONFIG_DEVICE_PROPERTY)))

device_config_t *lookup_device_config(char *identifier);

/* A changer definition */
typedef enum {
    CHANGER_CONFIG_COMMENT,
    CHANGER_CONFIG_TAPEDEV,
    CHANGER_CONFIG_TPCHANGER,
    CHANGER_CONFIG_CHANGERDEV,
    CHANGER_CONFIG_CHANGERFILE,
    CHANGER_CONFIG_PROPERTY,
    CHANGER_CONFIG_DEVICE_PROPERTY,
    CHANGER_CONFIG_CHANGER_CONFIG
} changer_config_key;

/* opaque object */
char **get_changer_list(void);

typedef struct changer_config_s changer_config_t;

/* Given the name of the changer, return a changer_config_t object.  Returns NULL
 * if no matching changer exists.  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired changer
 * @returns: object or NULL
 */

changer_config_t *lookup_changer_config(char *identifier);

/* Given a changer_config and a key, return a pointer to the corresponding val_t.
 *
 * @param ttyp: the changer_config to examine
 * @param key: changer_config (one of the DEVICE_CONFIG_* constants)
 * @returns: pointer to value
 */
val_t *changer_config_getconf(changer_config_t *devconf, changer_config_key key);

/* Get the name of this changer_config.
 *
 * @param ttyp: the changer_config to examine
 * @returns: name of the changer_config
 */
char *changer_config_name(changer_config_t *devconf);

char *changer_config_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this changer_config?  This
 * applies to the specific parameter *within* the changer_config.
 *
 * @param key: changer_config_key
 * @returns: boolean
 */
#define changer_config_seen(devconf, key)       (val_t_seen(changer_config_getconf((devconf), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param devconf: the changer_config to examine
 * @returns: various
 */

#define changer_config_get_comment(devconf)   (val_t_to_str(changer_config_getconf((devconf), CHANGER_CONFIG_COMMENT)))
#define changer_config_get_tapedev(devconf)   (val_t_to_str(changer_config_getconf((devconf), CHANGER_CONFIG_TAPEDEV)))
#define changer_config_get_tpchanger(devconf)   (val_t_to_str(changer_config_getconf((devconf), CHANGER_CONFIG_TPCHANGER)))
#define changer_config_get_changerdev(devconf)   (val_t_to_str(changer_config_getconf((devconf), CHANGER_CONFIG_CHANGERDEV)))
#define changer_config_get_changerfile(devconf)   (val_t_to_str(changer_config_getconf((devconf), CHANGER_CONFIG_CHANGERFILE)))

changer_config_t *lookup_changer_config(char *identifier);

/* A interacrtivity interface */
typedef enum interactivity_e  {
    INTERACTIVITY_COMMENT,
    INTERACTIVITY_PLUGIN,
    INTERACTIVITY_PROPERTY,
    INTERACTIVITY_INTERACTIVITY
} interactivity_key;

/* opaque object */
typedef struct interactivity_s interactivity_t;

/* Given the name of the interactivity, return a interactivity object.
 *  Returns NULL if no matching interactivity exists.
 *  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired interactivity
 * @returns: object or NULL
 */

interactivity_t *lookup_interactivity(char *identifier);

/* Given a interactivity and a key, return a pointer to the corresponding val_t.
 *
 * @param ttyp: the interactivity to examine
 * @param key: interactivity (one of the INTERACTIVITY_* constants)
 * @returns: pointer to value
 */
val_t *interactivity_getconf(interactivity_t *app, interactivity_key key);

/* Get the name of this interactivity.
 *
 * @param ttyp: the interactivity to examine
 * @returns: name of the interactivity
 */
char *interactivity_name(interactivity_t *app);

char *interactivity_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this interactivity?
 * This applies to the specific parameter *within* the interactivity.
 *
 * @param key: interactivity_key
 * @returns: boolean
 */
#define interactivity_seen(app, key)       (val_t_seen(interactivity_getconf((app), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param ttyp: the interactivity to examine
 * @returns: various
 */
#define interactivity_get_comment(interactivity)  (val_t_to_str(interactivity_getconf((interactivity), INTERACTIVITY_COMMENT))
#define interactivity_get_plugin(interactivity)   (val_t_to_str(interactivity_getconf((interactivity), INTERACTIVITY_PLUGIN)))
#define interactivity_get_property(interactivity) (val_t_to_proplist(interactivity_getconf((interactivity), INTERACTIVITY_PROPERTY)))

/* A taperscan interface */
typedef enum taperscan_e  {
    TAPERSCAN_COMMENT,
    TAPERSCAN_PLUGIN,
    TAPERSCAN_PROPERTY,
    TAPERSCAN_TAPERSCAN
} taperscan_key;

/* opaque object */
typedef struct taperscan_s taperscan_t;

/* Given the name of the taperscan, return a taperscan object.
 *  Returns NULL if no matching taperscan exists.
 *  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired taperscan
 * @returns: object or NULL
 */

taperscan_t *lookup_taperscan(char *identifier);

/* Given a taperscan and a key, return a pointer to the corresponding val_t.
 *
 * @param ttyp: the taperscan to examine
 * @param key: taperscan (one of the TAPERSCAN_* constants)
 * @returns: pointer to value
 */
val_t *taperscan_getconf(taperscan_t *app, taperscan_key key);

/* Get the name of this taperscan.
 *
 * @param ttyp: the taperscan to examine
 * @returns: name of the taperscan
 */
char *taperscan_name(taperscan_t *app);

char *taperscan_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this taperscan?
 * This applies to the specific parameter *within* the taperscan.
 *
 * @param key: taperscan_key
 * @returns: boolean
 */
#define taperscan_seen(app, key)       (val_t_seen(taperscan_getconf((app), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param ttyp: the taperscan to examine
 * @returns: various
 */
#define taperscan_get_comment(taperscan)  (val_t_to_str(taperscan_getconf((taperscan), TAPERSCAN_COMMENT)))
#define taperscan_get_plugin(taperscan)   (val_t_to_str(taperscan_getconf((taperscan), TAPERSCAN_PLUGIN)))
#define taperscan_get_property(taperscan) (val_t_to_proplist(taperscan_getconf((taperscan), TAPERSCAN_PROPERTY)))


/* A policy interface */
typedef enum policy_e  {
    POLICY_COMMENT,
    POLICY_RETENTION_TAPES,
    POLICY_RETENTION_DAYS,
    POLICY_RETENTION_RECOVER,
    POLICY_RETENTION_FULL,
    POLICY_POLICY
} policy_key;


/* opaque object */
typedef struct policy_s policy_s;

/* Given the name of the policy, return a policy object.
 *  Returns NULL if no matching policy exists.
 *  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired policy
 * @returns: object or NULL
 */

policy_s *lookup_policy(char *identifier);

/* Given a policy and a key, return a pointer to the corresponding val_t.
 *
 * @param ttyp: the policy to examine
 * @param key: policy (one of the TAPERSCAN_* constants)
 * @returns: pointer to value
 */
val_t *policy_getconf(policy_s *app, policy_key key);

/* Get the name of this policy.
 *
 * @param ttyp: the policy to examine
 * @returns: name of the policy
 */
char *policy_name(policy_s *app);

char *policy_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this policy?
 * This applies to the specific parameter *within* the policy.
 *
 * @param key: policy_key
 * @returns: boolean
 */
#define policy_seen(app, key)       (val_t_seen(policy_getconf((app), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param ttyp: the policy to examine
 * @returns: various
 */
#define policy_get_comment(policy)  (val_t_to_str(policy_getconf((policy), POLICY_COMMENT)))
#define policy_get_retention_tapes(policy)  (val_t_to_int(policy_getconf((policy), POLICY_RETENTION_TAPES)))
#define policy_get_retention_days(policy)  (val_t_to_int(policy_getconf((policy), POLICY_RETENTION_DAYS)))
#define policy_get_retention_recover(policy)  (val_t_to_int(policy_getconf((policy), POLICY_RETENTION_RECOVER)))
#define policy_get_retention_full(policy)  (val_t_to_int(policy_getconf((policy), POLICY_RETENTION_FULL)))


/* A storage interface */
typedef enum storage_e  {
    STORAGE_COMMENT,
    STORAGE_POLICY,
    STORAGE_TAPEDEV,
    STORAGE_TPCHANGER,
    STORAGE_LABELSTR,
    STORAGE_AUTOLABEL,
    STORAGE_META_AUTOLABEL,
    STORAGE_TAPEPOOL,
    STORAGE_RUNTAPES,
    STORAGE_TAPERSCAN,
    STORAGE_TAPETYPE,
    STORAGE_MAX_DLE_BY_VOLUME,
    STORAGE_TAPERALGO,
    STORAGE_TAPER_PARALLEL_WRITE,
    STORAGE_EJECT_VOLUME,
    STORAGE_ERASE_VOLUME,
    STORAGE_DEVICE_OUTPUT_BUFFER_SIZE,
    STORAGE_AUTOFLUSH,
    STORAGE_FLUSH_THRESHOLD_DUMPED,
    STORAGE_FLUSH_THRESHOLD_SCHEDULED,
    STORAGE_TAPERFLUSH,
    STORAGE_REPORT_USE_MEDIA,
    STORAGE_REPORT_NEXT_MEDIA,
    STORAGE_INTERACTIVITY,
    STORAGE_SET_NO_REUSE,
    STORAGE_DUMP_SELECTION,
    STORAGE_ERASE_ON_FAILURE,
    STORAGE_ERASE_ON_FULL,
    STORAGE_VAULT_LIST,
    STORAGE_STORAGE
} storage_key;


/* opaque object */
typedef struct storage_s storage_t;

storage_t *get_first_storage(void);
storage_t *get_next_storage(storage_t *st);
char **get_storage_list(void);

/* Given the name of the storage, return a storage object.
 *  Returns NULL if no matching storage exists.
 *  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired storage
 * @returns: object or NULL
 */

storage_t *lookup_storage(char *identifier);

/* Given a storage and a key, return a pointer to the corresponding val_t.
 *
 * @param ttyp: the storage to examine
 * @param key: storage (one of the TAPERSCAN_* constants)
 * @returns: pointer to value
 */
val_t *storage_getconf(storage_t *app, storage_key key);

/* Get the name of this storage.
 *
 * @param ttyp: the storage to examine
 * @returns: name of the storage
 */
char *storage_name(storage_t *app);

char *storage_key_to_name(int parm);

/* (convenience macro) has this parameter been seen in this storage?
 * This applies to the specific parameter *within* the storage.
 *
 * @param key: storage_key
 * @returns: boolean
 */
#define storage_seen(app, key)       (val_t_seen(storage_getconf((app), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param ttyp: the storage to examine
 * @returns: various
 */
#define storage_get_comment(storage)  (val_t_to_str(storage_getconf((storage), STORAGE_COMMENT)))
#define storage_get_policy(storage)  (val_t_to_str(storage_getconf((storage), STORAGE_POLICY)))
#define storage_get_tapedev(storage)  (val_t_to_str(storage_getconf((storage), STORAGE_TAPEDEV)))
#define storage_get_tpchanger(storage)  (val_t_to_str(storage_getconf((storage), STORAGE_TPCHANGER)))
#define storage_get_labelstr(storage)  (val_t_to_labelstr(storage_getconf((storage), STORAGE_LABELSTR)))
#define storage_get_autolabel(storage)  (val_t_to_autolabel(storage_getconf((storage), STORAGE_AUTOLABEL)))
#define storage_get_meta_autolabel(storage)  (val_t_to_str(storage_getconf((storage), STORAGE_META_AUTOLABEL)))
#define storage_get_tapepool(storage)  (val_t_to_str(storage_getconf((storage), STORAGE_TAPEPOOL)))
#define storage_get_runtapes(storage)  (val_t_to_int(storage_getconf((storage), STORAGE_RUNTAPES)))
#define storage_get_taperscan(storage)  (val_t_to_str(storage_getconf((storage), STORAGE_TAPERSCAN)))
#define storage_get_tapetype(storage)  (val_t_to_str(storage_getconf((storage), STORAGE_TAPETYPE)))
#define storage_get_max_dle_by_volume(storage)  (val_t_to_int(storage_getconf((storage), STORAGE_MAX_DLE_BY_VOLUME)))
#define storage_get_taperalgo(storage)  (val_t_to_taperalgo(storage_getconf((storage), STORAGE_TAPERALGO)))
#define storage_get_taper_parallel_write(storage)  (val_t_to_int(storage_getconf((storage), STORAGE_TAPER_PARALLEL_WRITE)))
#define storage_get_eject_volume(storage)  (val_t_to_boolean(storage_getconf((storage), STORAGE_EJECT_VOLUME)))
#define storage_get_erase_volume(storage)  (val_t_to_boolean(storage_getconf((storage), STORAGE_ERASE_VOLUME)))
#define storage_get_device_output_buffer_size(storage)  (val_t_to_size(storage_getconf((storage), STORAGE_DEVICE_OUTPUT_BUFFER_SIZE)))
#define storage_get_autoflush(storage)  (val_t_to_no_yes_all(storage_getconf((storage), STORAGE_AUTOFLUSH)))
#define storage_get_flush_threshold_dumped(storage)  (val_t_to_int(storage_getconf((storage), STORAGE_FLUSH_THRESHOLD_DUMPED)))
#define storage_get_flush_threshold_scheduled(storage)  (val_t_to_int(storage_getconf((storage), STORAGE_FLUSH_THRESHOLD_SCHEDULED)))
#define storage_get_taperflush(storage)  (val_t_to_int(storage_getconf((storage), STORAGE_TAPERFLUSH)))
#define storage_get_report_use_media(storage)  (val_t_to_bool(storage_getconf((storage), STORAGE_REPORT_USE_MEDIA)))
#define storage_get_report_next_media(storage)  (val_t_to_bool(storage_getconf((storage), STORAGE_REPORT_NEXT_MEDIA)))
#define storage_get_interactivity(storage)  (val_t_to_str(storage_getconf((storage), STORAGE_INTERACTIVITY)))
#define storage_get_set_no_reuse(storage)  (val_t_to_bool(storage_getconf((storage), STORAGE_SET_NO_REUSE)))
#define storage_get_dump_selection(storage)  (val_t_to_dump_selection(storage_getconf((storage), STORAGE_DUMP_SELECTION)))
#define storage_get_erase_on_failure(storage)  (val_t_to_boolean(storage_getconf((storage), STORAGE_ERASE_ON_FAILURE)))
#define storage_get_erase_on_full(storage)  (val_t_to_boolean(storage_getconf((storage), STORAGE_ERASE_ON_FULL)))
#define storage_get_vault_list(storage)  (val_t_to_vault_list(storage_getconf((storage), STORAGE_VAULT_LIST)))

/* A catalog interface */
typedef enum catalog_e  {
    CATALOG_COMMENT,
    CATALOG_PLUGIN,
    CATALOG_PROPERTY,
    CATALOG_CATALOG
} catalog_key;

/* opaque object */
typedef struct catalog_s catalog_t;

/* Given the name of the catalog, return a catalog object.
 *  Returns NULL if no matching catalog exists.
 *  Note that the match is case-insensitive.
 *
 * @param identifier: name of the desired catalog
 * @returns: object or NULL
 */

catalog_t *lookup_catalog(char *identifier);

/* Given a catalog and a key, return a pointer to the corresponding val_t.
 *
 * @param ttyp: the catalog to examine
 * @param key: catalog (one of the CATALOG_* constants)
 * @returns: pointer to value
 */
val_t *catalog_getconf(catalog_t *app, catalog_key key);

/* Get the name of this catalog.
 *
 * @param ttyp: the catalog. to examine
 * @returns: name of the catalog.
 */
char *catalog_name(catalog_t *app);

/* (convenience macro) has this parameter been seen in this catalog?
 * This applies to the specific parameter *within* the catalog.
 *
 * @param key: catalog_key
 * @returns: boolean
 */
#define catalog_seen(app, key)       (val_t_seen(catalog_getconf((app), (key))))

/* (convenience macros)
 * fetch a particular parameter; caller must know the correct type.
 *
 * @param ttyp: the catalog to examine
 * @returns: various
 */
#define catalog_get_comment(catalog)  (val_t_to_str(catalog_getconf((catalog), CATALOG_COMMENT))
#define catalog_get_plugin(catalog)   (val_t_to_str(catalog_getconf((catalog), CATALOG_PLUGIN)))
#define catalog_get_property(catalog) (val_t_to_proplist(catalog_getconf((catalog), CATALOG_PROPERTY)))


/*
 * Error Handling
 */

typedef enum {
    /* No errors or warnings */
    CFGERR_OK = 0,

    /* warnings were encountered */
    CFGERR_WARNINGS = 1,

    /* errors (and maybe some warnings too, who knows) were encountered */
    CFGERR_ERRORS = 2,
} cfgerr_level_t;

/*
 * Errors
 */

/* Get a GSList of all error and warning messages accumulated so far.
 *
 * @param (output) errlist: pointer to the list of error strings; allocated
 * memory remains the responsibility of the config module.  If errlist is
 * NULL, the list is not returned.
 * @returns: current error level
 */
cfgerr_level_t config_errors(GSList **errlist);

/* Clear any error conditions.
 */
void config_clear_errors(void);

/* Print the list of current error and warning messages, one per line,
 * to stderr. This is a convenience function for command-line
 * applications.
 */
void config_print_errors(void);
void config_print_errors_as_message(void);

/* Add an error message to the list of errors, and make sure tha the
 * error level is at least LEVEL.  This is used by the diskfile module
 * to insert its errors into this module's error list.
 *
 * @param level: level for this error
 * @param errmsg: error message; conffile takes responsibility for freeing
 *   this string.
 */
void config_add_error(cfgerr_level_t level, char *errmsg);

/*
 * Command-line handling
 */

/* opaque type */
typedef struct config_overrides_s config_overrides_t;

/* Create a new, empty config_overrides object.
 *
 * @param size_estimate: a guess at the number of overwrites; argc/2 is a 
 *  good estimate.
 * @returns: new object
 */
config_overrides_t *new_config_overrides(int size_estimate);

/* Free a config_overrides object.  This usually won't be needed, as
 * apply_config_overrides takes ownership of the overwrites for you.
 *
 * @param co: config_overrides object
 */
void free_config_overrides(config_overrides_t *co);

/* Add an overwrite to a config_overrides object.
 *
 * @param co: the config_overrides object
 * @param key: the configuration parameter's key, possibly with the format
 * SUBTYPE:NAME:KEYWORD
 * @param value: the value for the parameter, as would be seen in amanda.conf
 */
void add_config_override(config_overrides_t *co,
			 char *key,
			 char *value);

/* Add an overwrite option from the command line to a config_overrides
 * object.  Calls error() with any errors
 *
 * @param co: the config_overrides object
 * @param optarg: the value of the command-line option
 */
void add_config_override_opt(config_overrides_t *co,
			      char *optarg);

/* Given a command line, represented as argc/argv, extract any -o options
 * as config overwrites.  This function modifies argc and argv in place.
 *
 * This is the deprecated way to extract config overwrites, for applications
 * which do not use getopt.  The preferred method is to use getopt and
 * call add_config_override_opt for any -o options.
 *
 * @param argc: (in/out) command-line length
 * @param argv: (in/out) command-line strings
 * @returns: newly allocated config_overrides object
 */
config_overrides_t *
extract_commandline_config_overrides(int *argc,
				      char ***argv);

/* Set configuration overwrites to the current configuration and take
 * ownership of the config_overrides object.
 *
 * @param co: the config_overrides object
 */
void set_config_overrides(config_overrides_t *co);

/*
 * Initialization
 */

/* If the config is initialized */
gboolean
config_is_initialized(void);

/* Constants for config_init */
typedef enum {
    /* Use arg_config_name, if not NULL */
    CONFIG_INIT_EXPLICIT_NAME = 1 << 0,

    /* Use the current working directory if an explicit name is not available */
    CONFIG_INIT_USE_CWD = 1 << 1,

    /* This is a client application (server is default) */
    CONFIG_INIT_CLIENT = 1 << 2,

    /* New configuration should "overlay" existing configuration; this
     * is used by clients to load multiple amanda-client.conf files. */
    CONFIG_INIT_OVERLAY = 1 << 3,

    /* Load the global config */
    CONFIG_INIT_GLOBAL = 1 << 4,

    /* Do not generate errors (internal use) */
    CONFIG_OVERRDIDE_NO_ERROR = 1 << 5,
} config_init_flags;

/* Initialize this application's configuration, with the specific actions
 * based on 'flags':
 *  - if CONFIG_INIT_OVERLAY is not set, configuration values are reset
 *    to their defaults
 *  - if CONFIG_INIT_EXPLICIT_NAME and arg_config_name is not NULL,
 *    use CONFIG_DIR/arg_config_name as config_dir arg_config_name as 
 *    config_name.
 *  - otherwise, if CONFIG_USE_CWD is set, use the directory in which 
 *    the application was started as config_dir, and its filename as 
 *    config_name.
 *  - otherwise, for the client only, se config_dir to CONFIG_DIR and
 *    config_name to NULL.
 *  - depending on CONFIG_INIT_CLIENT, read amanda.conf or amanda-client.conf
 *
 * @param flags: flags indicating desired behavior, as above
 * @param arg_config_name: config name to use (from e.g., argv[1])
 * @returns: current error level
 */
cfgerr_level_t config_init(
	 config_init_flags flags,
	 char *arg_config_name);
cfgerr_level_t config_init_with_global(
	 config_init_flags flags,
	 char *arg_config_name);

/* Free all memory allocated for the configuration.  This effectively
 * reverses the effects of config_init().
 */
void config_uninit(void);

/* Encode any applied config_overrides into a strv format suitale for
 * executing another Amanda tool.
 *
 * The * result is dynamically allocated and NULL terminated.  There is no
 * provision to free the result, as this function is always called just
 * before execve(..).
 *
 * First gives the number of array elements to leave for the caller to
 * fill in.  The usual calling pattern is this:
 *   command_line = get_config_options(3);
 *   command_line[0] = "appname";
 *   command_line[1] = config_name;
 *   command_line[2] = "--foo";
 *   execve(command_line[0], command_line, safe_env());
 *
 * @param first: number of unused elements to leave at the beginning of
 * the array.
 * @returns: NULL-terminated string array suitable for execve
 */
char **get_config_options(int first);

/* Get the config name */
char *get_config_name(void);

/* Get the config directory */
char *get_config_dir(void);

/* Get the config filename */
char *get_config_filename(void);

char *get_running_on(void);

/*
 * Utilities
 */

/* Security plugins get their configuration information through a callback
 * with the signature:
 *   char *callback(char *key, void *userpointer);
 * where key is the name of the desired parameter, which may not match the
 * name used in this module.  See the implementations of these functions
 * to learn which keys they support, or to add new keys.
 */
char *generic_client_get_security_conf(char *, void *);
char *generic_get_security_conf(char *, void *);

/* Dump the current configuration information to stdout, in a format 
 * that can be re-read by this module.  The results will include any
 * command-line overwrites.
 *
 * This function only dumps the server configuration, and will fail on
 * clients.
 */
void dump_configuration(gboolean print_default, gboolean print_source);

void dump_dumptype(dumptype_t *dp, char *prefix, gboolean print_default,
		   gboolean print_source);

/* Return a sequence of strings giving the printable representation
 * of the given val_t.  If str_needs_quotes is true and each string is
 * prefixed by the relevant configuration keyword, these strings will
 * be parseable by this module, and will reproduce exactly the same
 * configuration value.  See the implementation of dump_configuration
 * for details.
 *
 * If str_needs_quotes is provided, a CONFTYPE_STR value will be returned with 
 * quotes.
 *
 * The result is a NULL-terminated strv, which can be freed with g_strfreev or
 * joined with g_strjoinv.  Caller is responsible for freeing the memory.
 *
 * @param val: the value to analyze
 * @param str_needs_quotes: add quotes to CONFTYPE_STR values?
 * @returns: NULL-terminated string vector
 */
char **val_t_display_strs(val_t *val, int str_needs_quotes, gboolean  print_source,
			  gboolean print_unit);

/* Read a dumptype; this is used by this module as well as by diskfile.c to
 * read the disklist.  The two are carefully balanced in their parsing process.
 *
 * Nobody else should use this function.  Seriously.
 */
dumptype_t *read_dumptype(char *name, FILE *from, char *fname, int *linenum);

/* Every call return a pointer to a string with an increasing number; this is
 * used by this module as well as by diskfile.c to read the disklist.
 *
 * Nobody else should use this function.  Seriously.
 *
 * @returns: a pointer to a static string.
 */
char *anonymous_value(void);

/* Extend a relative filename with the current config_dir; if filename is already
 * absolute, this is equivalent to g_strdup.
 *
 * @param filename: filename to extend
 * @returns: newly allocated filename
 */
char *config_dir_relative(char *filename);

/* Convert from a symbol back to a name for logging and for dumping
 * config values
 *
 * @param taperalgo: the constant value
 * @returns: statically allocated string
 */
char *taperalgo2str(taperalgo_t taperalgo);

/* Looks for a unit value like b, byte, bytes, bps, etc. Technically
 * the return value should never be < 1, but we return a signed value
 * to help mitigate bad C promotion semantics. Returns 0 on error.
 *
 * This is here in this module because it uses the numb_keytable.
 *
 * @param casestr: the unit string
 * @returns: the corresponding multiplier (e.g., 'M' => 1024*1024)
 */
gint64 find_multiplier(char * casestr);

/* Converts a string matching any of Amanda's names for "true" or
 * "false" to a boolean value.
 *
 * @param str: string to match
 * @returns: 0 or 1 (boolean) or -1 (no match)
 */
int string_to_boolean(const char *str);

/* Return a pointer to a static string for the data_path */
char *data_path_to_string(data_path_t data_path);

/* Return the data_path for the string */
data_path_t data_path_from_string(char *data);

void free_property_t(gpointer p);

/* Converts a string into Amanda property name style.
 *
 * @param name: The name to convert.
 * @returns: A newly allocated string, with name in lowercase and
 * any instances of '_' replaced with '-'.
 */
gchar *amandaify_property_name(const gchar *name);

/**
 * Returns a string representation of a CONFTYPE_EXECUTE_ON property with a
 * given separator. It is up to the caller to free that string if it so desires.
 *
 * @param flags: the CONFTYPE_EXECUTE_ON property.
 * @param separator: the separator to use.
 * @returns: the string representation.
 */

char *execute_on_to_string(int flags, char *separator);

char *custom_escape(char *str);

val_t *getconf_human(confparm_key key);
val_t *dumptype_getconf_human(dumptype_t *typ, dumptype_key key);
val_t *tapetype_getconf_human(tapetype_t *typ, tapetype_key key);
val_t *application_getconf_human(application_t *typ, application_key key);
val_t *device_config_getconf_human(device_config_t *typ, device_config_key key);
val_t *changer_config_getconf_human(changer_config_t *typ, changer_config_key key);
val_t *storage_getconf_human(storage_t *typ, storage_key key);
val_t *pp_script_getconf_human(pp_script_t *typ, pp_script_key key);
val_t *holdingdisk_getconf_human(holdingdisk_t *typ, holdingdisk_key key);
val_t *interface_getconf_human(interface_t *typ, interface_key key);
val_t *interactivity_getconf_human(interactivity_t *typ, interactivity_key key);
val_t *taperscan_getconf_human(taperscan_t *typ, taperscan_key key);
val_t *policy_getconf_human(policy_s *typ, policy_key key);

#endif /* ! CONFFILE_H */
